' IndiaStockQuotes Component
' Author : Anand Mukundan, http://www.dotnetindia.com
' If you use this component in a commercial software, you need to carry a mention of the usage of this component (with a link to its hosted page)
' in some part of the of your application that is visible when your application is run (Like the About box or the Splash Screen).

Imports System.Runtime.InteropServices
Imports System.Xml
Imports Microsoft.VisualBasic.FileIO
Imports System.Drawing

Namespace Yahoo
    <ComVisible(True)> _
    Public Class StockQuotes
        Implements IStockQuoteProvider


        Public Function GetStockQuote(ByVal stockSymbol As String, Optional ByVal stockExchange As IndiaExchange = 0) As StockQuoteData Implements IStockQuoteProvider.GetStockQuote

            Dim URL As String = "http://in.finance.yahoo.com/q?s={0}"

            ' if Exchange is passed then we use the extra querystring parameter. If no exchange is passed then 
            'we assume that the exchange information is in the symbol itself
            If stockExchange = IndiaExchange.BSE Then
                'Change in Yahoo now needs the exact symbol match with extension for this to work
                If stockSymbol.EndsWith(".bo") = False Then
                    stockSymbol += ".bo"
                End If
                URL += "&m=BO"
            ElseIf stockExchange = IndiaExchange.NSE Then
                'Change in Yahoo now needs the exact symbol match with extension for this to work
                If stockSymbol.EndsWith(".ns") = False Then
                    stockSymbol += ".ns"
                End If
                URL += "&m=NS"
            End If

            URL = String.Format(URL, stockSymbol)

                Dim PageData As String = GetURLString(URL)

                ''TODO: This calls the website again. Can optimize to use the already retrieved string
                If IsValidStockSymbol(stockSymbol, PageData) = False Then
                    ThrowInvalidStockSymbol(stockSymbol)
                End If
                ' Get Stock Price
                Dim LastTradeBegin As Integer = PageData.IndexOf("Last Trade:</td><td class=""yfnc_tabledata1""><big><b>") + 52
                Dim LastTradeEnd As Integer = PageData.IndexOf("<", LastTradeBegin)
                Dim LastTradeRate As String = PageData.Substring(LastTradeBegin, LastTradeEnd - LastTradeBegin)

                'Get Change
                'First look for the downward color setting
                Dim ChangeDownward As Boolean = True
                Dim ChangeBegin As Integer = PageData.IndexOf("<b style=""color:#cc0000;"">", LastTradeEnd)
                If ChangeBegin < 0 Then
                    'Stock Moved upward, so look for Upward	movement
                    ChangeBegin = PageData.IndexOf("<b style=""color:#008800;"">")
                    ChangeDownward = False
                End If
                ChangeBegin += 26
                Dim ChangeEnd As Integer = PageData.IndexOf("<", ChangeBegin)
                Dim Change As String = PageData.Substring(ChangeBegin, ChangeEnd - ChangeBegin)

                'Trade Time
                Dim TradeTimeBegin As Integer = PageData.IndexOf("Trade Time:</td><td class=""yfnc_tabledata1"">") + 44
                Dim TradeTimeEnd As Integer = PageData.IndexOf("<", TradeTimeBegin)
                Dim TradeDate As DateTime
                Try
                    TradeDate = DateTime.Parse(PageData.Substring(TradeTimeBegin, TradeTimeEnd - TradeTimeBegin))
                Catch ex As Exception
                    'Since sometime we are getting the incorrect Date time from Yahoo, instead of throwing an exception
                    'I am putting a fixed date value, so that the remaining data can still be used.
                    TradeDate = DateTime.Parse("1/1/1900")
                End Try


                'Fill and return data
                Dim SData As New StockQuoteData
                SData.Symbol = stockSymbol
                SData.StockRate = Decimal.Parse(LastTradeRate)
                SData.StockChangeDownwards = ChangeDownward
                Try
                    SData.StockChangeValue = Decimal.Parse(Change.Substring(0, _
                        Change.IndexOf("(")))
                    Dim cst As Integer, cend As Integer
                    cst = Change.IndexOf("(") + 1
                    cend = Change.IndexOf("%")
                    SData.StockChangePercent = Decimal.Parse(Change.Substring(cst, cend - cst))
                Catch ex As Exception
                    ' if the change value is 0, we sometimes get text instead of a value. This is a shortcut as of now
                    SData.StockChangeValue = 0
                    SData.StockChangePercent = 0
                End Try

                SData.DateOfTrade = TradeDate
                Return SData
        End Function

        Public Function GetStockCharts(ByVal stockSymbol As String, ByVal ChartDuration As ChartDurationType) As Image Implements IStockQuoteProvider.GetStockCharts
            'Currently supports only for NSE. BSE seems to have less Chart Types in Yahoo.
            Dim ChartImage As Image = Nothing
            If String.IsNullOrEmpty(stockSymbol) Then
                Return ChartImage
            End If

            If stockSymbol.IndexOf(".NS", StringComparison.InvariantCultureIgnoreCase) < 0 Then
                ' if we dont have .NS we add it as the Symbol function expects a fully qualified symbol.
                stockSymbol += ".NS"
            End If
            Dim BaseData As Byte()
            Dim Url As String = "http://ichart.finance.yahoo.com/{0}?s={1}"
            If IsValidStockSymbol(stockSymbol) = False Then
                ThrowInvalidStockSymbol(stockSymbol)
            End If

            Select Case ChartDuration
                Case ChartDurationType.OneDay
                    Url = String.Format(Url, "b", stockSymbol)
                Case ChartDurationType.ThreeMonth
                    Url = String.Format(Url, "c/3m/p", stockSymbol)
                Case ChartDurationType.SixMonth
                    Url = String.Format(Url, "c/6m/p", stockSymbol)
                Case ChartDurationType.OneYear
                    Url = String.Format(Url, "c/1y/p", stockSymbol)
                Case ChartDurationType.TwoYear
                    Url = String.Format(Url, "c/2y/p", stockSymbol)
                Case ChartDurationType.FiveYear
                    Url = String.Format(Url, "c/5y/p", stockSymbol)
            End Select

            BaseData = GetURLData(Url)
            ChartImage = ByteArrayToImage(BaseData)
            Return ChartImage
        End Function

        Private Shared Sub ThrowInvalidStockSymbol(ByVal stockSymbol As String)
            ' We got a wrong symbol, throw the Invalid Symbol Exception
            Dim ex As New InvalidSymbolException
            ex.Data.Add("Symbol", stockSymbol)
            Throw ex
        End Sub

        Private Function IsValidStockSymbol(ByVal stockSymbol As String, Optional ByVal pPageData As String = "") As Boolean

            'Assumes the Stock Symbol also includes the Exchange integrated. We are using the Search for Symbol Page as this is smaller and 
            'so performs better than doing a Quote to check the validity of a symbol
            'Also this depends on the symbol being exactly right to return only 1 match to be valid

            Dim Url As String = "http://in.finance.yahoo.com/lookup?s={0}&t=S&m=ALL"

            Url = String.Format(Url, stockSymbol)
            Dim PageData As String

            If pPageData.Length = 0 Then
                PageData = GetURLString(Url)
            Else
                PageData = pPageData
                'If the page already has quote data then the symbol is obviously valid
                If PageData.IndexOf("Delayed quote data") > 0 Then
                    Return True
                End If
            End If

            'Check for Symbol lookup page status
            If String.IsNullOrEmpty(PageData) = True Or PageData.IndexOf(String.Format("Your search for '{0}' returned ", stockSymbol)) > 0 Or _
                            PageData.IndexOf(String.Format("1 results for <b>'{0}'</b> ", stockSymbol)) < 0 Then
                Return False
            Else
                Return True
            End If
        End Function
    End Class

End Namespace
